package PrjDBConf

import (
    "dgo/MyProj/PrjDBUtils"
    db_utils "dgo/goutils/db-utils"
    "dgo/goutils/utils"
    "gitee.com/ymofen/gobase/golog"
    "gitee.com/ymofen/supervalue"
    "time"
)

var (
    CREATE_TABLE_DBCONF_MYSQL string = `
CREATE TABLE sys_config (
  FID varchar(50) COLLATE utf8_unicode_ci,
  FSN int(11) DEFAULT '0',
  FTypeID varchar(30) COLLATE utf8_unicode_ci NOT NULL,
  FAppID varchar(30) COLLATE utf8_unicode_ci NOT NULL,
  FCaption varchar(30) ,
  FConfig text COLLATE utf8_unicode_ci,
  FState int(11) DEFAULT NULL,
  FLastUptime datetime DEFAULT NULL COMMENT '最后更新时间',
  PRIMARY KEY (FTypeID,FAppID)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
SET FOREIGN_KEY_CHECKS=1;
`

    CREATE_TABLE_DBCONF_SQLITE string = `
CREATE TABLE "main"."sys_config" (
"FID"  TEXT(50),
"FSN"  INTEGER,
"FTypeID"  TEXT(30) NOT NULL,
"FAppID"  TEXT(30) NOT NULL,
"FCaption"  TEXT(30),
"FConfig"  TEXT(5000),
"FState"  INTEGER,
"FLastUptime"  INTEGER,
PRIMARY KEY ("FTypeID", "FAppID")
)
;
`
)

type DBConf struct {
    dbconf_is_table_ok bool
    DBCONF_DB_ID       string
}

func NewDBConf(dbid string) *DBConf {
    rval := &DBConf{
        DBCONF_DB_ID: dbid,
    }
    return rval
}

func (this *DBConf) CheckEnv() error {
    if !this.dbconf_is_table_ok {
        sqltype := PrjDBUtils.GetDBDriverName(this.DBCONF_DB_ID)
        if sqltype == "sqlite3" {
            err := PrjDBUtils.CheckCreateTableByID(this.DBCONF_DB_ID, "sys_config", CREATE_TABLE_DBCONF_SQLITE)
            if err != nil {
                return err
            }
        } else {
            err := PrjDBUtils.CheckCreateTableByID(this.DBCONF_DB_ID, "sys_config", CREATE_TABLE_DBCONF_MYSQL)
            if err != nil {
                return err
            }
        }

        this.dbconf_is_table_ok = true
    }
    return nil
}

func (this *DBConf) ConfUpdate(appid, typeid, conf string) error {
    recB := db_utils.NewRecUpdateUpbuilder()
    recB.SetValues("FLastUptime", time.Now())
    recB.SetValues("fconfig", conf, "ftypeid", typeid, "fappid", appid, "fid", appid)
    db, err := PrjDBUtils.GetDBConn(this.DBCONF_DB_ID)
    if err != nil {
        return err
    }
    defer db.Close()
    _, errsql, err := db.ExecuteUpOrInsertByDB(recB, "fappid;ftypeid", "sys_config")
    if err != nil {
        utils.Log.WarnTypef("APPCONF", "更新配置出现异常:%s\n%s", err.Error(), errsql)
        return err
    }
    if ConfUpdateCallBack != nil {
        ConfUpdateCallBack(appid, typeid, conf)
    }
    return nil
}

func (this *DBConf) innerGetConfList(typeid string) (error, *wrapper.SuperValue) {
    db, err := PrjDBUtils.GetDBConn(DBConf_DB_ID)
    if err != nil {
        return err, nil
    }
    defer db.Close()
    sqlB := db_utils.NewSQLBuilder()
    sqlB.SelectFields("fcaption, fappid, fid, ftypeid").From("sys_config")
    sqlB.Where().EqualValues("ftypeid", typeid)

    rows, err := db.GetRowList(sqlB.String())
    if err != nil {
        return err, nil
    }

    if rows == nil || len(rows) == 0 {
        return nil, nil
    }

    rval := wrapper.NewSVObject()
    lstconf := rval.ForceArrayByPath("list")
    for i := 0; i < len(rows); i++ {
        row := rows[i]
        itm := lstconf.AppendObject()
        itm.SetKeyValues("caption", row[0].AsString(""), "appid", row[1].AsString(""), "typeid", typeid, "confAs", "json")
        if len(itm.StringByName("caption", "")) == 0 {
            itm.SetKeyValues("caption", row[1].AsString(""))
        }
        itm.SetKeyString("id", row[1].AsString(""))
    }
    return nil, rval
}

func (this *DBConf) GetConfByID(appid, strTypeID string) (error, []byte) {
    if appid == "list" {
        err, rval := this.innerGetConfList(strTypeID)
        if err != nil {
            return err, nil
        } else if rval == nil {
            return nil, nil
        } else {
            return nil, rval.AsJSONBytes(false)
        }

    }
    sqlB := db_utils.NewSQLBuilder()
    sqlB.SelectFields("fconfig").From("sys_config")
    sqlB.Where()
    sqlB.EqualValues("fappid", appid, "ftypeid", strTypeID)

    lvsql := sqlB.String()
    golog.Debugf(lvsql)

    blockItm := PrjDBUtils.DbBlocker.BeginUse2()
    defer blockItm.EndUse()
    db, err := PrjDBUtils.GetDB(DBConf_DB_ID)
    if err != nil {
        return err, nil
    }
    defer db.Close()

    lst, err := PrjDBUtils.GetRowList(db, lvsql)
    if err != nil {
        return err, nil
    }

    if lst == nil || len(lst) == 0 {
        return nil, nil
    }

    row := lst[0]
    return nil, row[0].AsBytes()
}

/*
**

	如果是JSON则返回supervalue
	否则返回ByteBuilder
*/
func (this *DBConf) GetConf(strAppID, strTypeID, strConfAsType string, newFlag int) (error, interface{}) {
    // 0 不新建, 1: 新建(如果appid对应不存在), 10: 新建Type(如果不存在则新建)

    if err := this.CheckEnv(); err != nil {
        return err, nil
    }

    if strAppID == "list" {
        err, rval := this.innerGetConfList(strTypeID)
        if err != nil {
            return err, nil
        } else if rval == nil {
            return nil, nil
        } else {
            return nil, rval
        }
    }

    sqlB := db_utils.NewSQLBuilder()
    sqlB.SelectFields("fconfig, fid, ftypeid, fappid, flastuptime").From("sys_config")
    sqlB.Where()
    //sqlB.AddAny("length(fconfig)>0 AND fconfig is not null AND ")
    sqlB.EqualValues("ftypeid", strTypeID)
    if len(strAppID) > 0 {
        sqlB.And().GBegin().EqualFieldMultiOr("fappid", strAppID, "", nil).GEnd()
    } else {
        sqlB.And().GBegin().EqualFieldMultiOr("fappid", "", nil).GEnd()
    }
    sqlB.OrderBy()
    if len(strAppID) > 0 {
        sqlB.AddAny(" length(fappid) = 0, ")
    }
    sqlB.Ascend("flastuptime")
    lvsql := sqlB.String()
    //golog.Debugf(lvsql)

    db, err := PrjDBUtils.GetDB(DBConf_DB_ID)
    if err != nil {
        return err, nil
    }
    defer db.Close()

    lst, err := PrjDBUtils.GetRowList(db, lvsql)
    if err != nil {
        return err, nil
    }

    if len(lst) == 0 { // 无记录
        err := NewDBConfRec(db, "", strTypeID, strAppID, int(newFlag))
        if err != nil {
            utils.Log.Warnf("添加配置[%s], newflag:%d, 出现了异常:%s", strTypeID, newFlag, err.Error())
        }
        return err, nil
    }

    var respConf *wrapper.SuperValue
    var txtB utils.BytesBuilder

    // appid 对应的标记
    exists_flag_1 := false

    // fconfig, fid, ftypeid, fappid
    PrjDBUtils.ForeachRows(lst, func(row []db_utils.SqlValue, idx int) bool {
        confBuf := row[0].AsBytes()
        if len(strAppID) > 0 && row[3].AsString("") == strAppID {
            exists_flag_1 = true
        }

        if len(confBuf) == 0 { // 空数据
            return true
        }

        if strConfAsType == "json" {
            conf, err := wrapper.NewValueFromJson(confBuf, false, false)

            if err != nil {
                utils.Log.Warnf("解析配置[%s-%s]出现异常:%s", row[1].AsString(""), strTypeID, err.Error())
                return true // 退出当前配置
            }
            //golog.Debugf("idx:%d, json:%s", idx, conf.StatusString())
            if respConf == nil {
                respConf = conf
            } else {
                respConf.MergeWith(conf, func(keyPath string, oldv *wrapper.SuperValue, newv *wrapper.SuperValue) wrapper.MergeOp {
                    if newv.IsObject() && (oldv == nil || oldv.IsObject()) {
                        //fmt.Printf("Normal:%s\n", keypath)
                        return wrapper.MO_Normal
                    }
                    return wrapper.MO_Replace
                })
            }

        } else {
            txtB.Write(confBuf)
        }

        return true
    })

    if newFlag == 1 && !exists_flag_1 {
        err := NewDBConfRec(db, "", strTypeID, strAppID, int(newFlag))
        if err != nil {
            utils.Log.Warnf("添加配置[%s], newflag:%d, 出现了异常:%s", strTypeID, newFlag, err.Error())
        }
    }

    if strConfAsType == "json" {
        if respConf == nil {
            return nil, nil
        }
        return nil, respConf
    } else {
        return nil, txtB
    }
}
